package com.suray.basic.wcs.plc.pojo.oper.single;

import com.suray.basic.wcs.plc.config.resolver.PlcAnnotation;
import com.suray.basic.wcs.plc.core.PlcOffset;
import com.suray.basic.wcs.plc.enums.*;
import com.suray.basic.wcs.plc.pojo.DeviceBase;
import com.suray.basic.wcs.utils.Coord;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.WcsFuncUtil;

import java.io.IOException;

/**
 * 单体输送线+外形检测
 */
public class ShapeConveyorOper extends DeviceBase {

    /*************************************************输送线**********************************************************/
    //设备类型
    private static final PlcOffset DEVICE_TYPE = new PlcOffset(0, null);
    //设置设备位移量
    private static final PlcOffset MOVEMENT = new PlcOffset(1, null);
    //设置输送线自动速度
    private static final PlcOffset CONVEYOR_AUTO_SPEED = new PlcOffset(8, null);
    //设置输送线自动加速时间
    private static final PlcOffset CONVEYOR_AUTO_ACC = new PlcOffset(9, null);
    //设置输送线自动减速时间
    private static final PlcOffset CONVEYOR_AUTO_DEC = new PlcOffset(10, null);
    //设置输送线手动速度
    private static final PlcOffset CONVEYOR_MANUAL_SPEED = new PlcOffset(11, null);
    //设置输送线手动加速时间
    private static final PlcOffset CONVEYOR_MANUAL_ACC = new PlcOffset(12, null);
    //设置输送手动减速时间
    private static final PlcOffset CONVEYOR_MANUAL_DEC = new PlcOffset(13, null);
    //单体设备运行（含自动门，翻转机构）
    private static final PlcOffset TASK_ACTION = new PlcOffset(14, null);
    //故障清除
    private static final PlcOffset CLEAR_ERROR = new PlcOffset(17, 0);
    //清除入库申请
    private static final PlcOffset CLEAR_ASK_IN_CARGO = new PlcOffset(17, 7);
    //系统急停
    private static final PlcOffset STOP_STATE = new PlcOffset(18, 0);
    //系统模式:自动、手动
    private static final PlcOffset AUTO_MODE = new PlcOffset(18, 1);
    //入库信号
    private static final PlcOffset ASK_IN_CARGO = new PlcOffset(18, 2);
    //手动输送线正转
    private static final PlcOffset MANUAL_FORWARD = new PlcOffset(18, 3);
    //手动输送线反转
    private static final PlcOffset MANUAL_REVERSE = new PlcOffset(18, 4);
    //WCS可控
    private static final PlcOffset IS_CONTROL = new PlcOffset(18, 5);
    //设备正在运行中
    private static final PlcOffset RUNNING = new PlcOffset(18, 6);
    //故障报警状态
    private static final PlcOffset ERROR_STATE = new PlcOffset(18, 7);
    //镜反光电2
    private static final PlcOffset IN_CARGO_SIGNAL = new PlcOffset(18, 9);
    //镜反光电1
    private static final PlcOffset OUT_CARGO_SIGNAL = new PlcOffset(18, 8);
    //故障码
    private static final PlcOffset ERROR_INFO = new PlcOffset(20, null);
    //校准位
    private static final PlcOffset CHECK_NO = new PlcOffset(21, null);
    //当前设备类型
    private static final PlcOffset CURRENT_DEVICE_TYPE = new PlcOffset(22, null);
    //输送线延迟停止时间
    private static final PlcOffset CONVERY_STOP_DELAY_TIME = new PlcOffset(23, null);
    //外形前超限延时时间
    private static final PlcOffset SHAPE_OVER_FRONT_TIME = new PlcOffset(24, null);
    //外形后超限延时时间
    private static final PlcOffset SHAPE_OVER_BACK_TIME = new PlcOffset(25, null);

    //任务号
    private static final PlcOffset TASK_NO = new PlcOffset(65, null);
    //起点设备编号
    private static final PlcOffset TASK_START_LOCATION = new PlcOffset(66, null);
    //终点设备编号
    private static final PlcOffset TASK_END_LOCATION = new PlcOffset(67, null);
    //下发输送任务
    private static final int SEND_TASK = 68;
    //清除任务信息
    private static final int CLEAR_TASK = 71;

    /********************************************条码*********************************************************/
    //条码27-43
    private static final PlcOffset SCAN_CAN_READ = new PlcOffset(19, 4);
    private final int maxBarCode = 15;

    private final static int highBit = 8;
    private final static int lowBit = 0;
    private final static int startNum = 27;

    /*************************************************设置故障**********************************************************/
    //清除扫码器故障
    private static final PlcOffset CLEAR_SCAN_ERROR = new PlcOffset(17, 4);
    //扫码故障灯
    private static final PlcOffset SCAN_ERROR = new PlcOffset(17, 3);
    //超重灯
    private static final PlcOffset WEIGHT_ERROR = new PlcOffset(17, 5);
    //清除超重故障灯
    private static final PlcOffset CLEAR_WEIGHT_ERROR = new PlcOffset(17, 6);
    //无任务故障灯
    private static final PlcOffset NO_TASK_ERROR = new PlcOffset(17, 8);
    //清除无任务故障灯
    private static final PlcOffset CLEAR_NO_TASK_ERROR = new PlcOffset(17, 9);


    /*************************************************外形检测**********************************************************/
    //外形检测开启
    private static final PlcOffset OPEN_SHAPE = new PlcOffset(17, 1);
    //关闭外形检测
    private static final PlcOffset CLOSE_SHAPE = new PlcOffset(17, 2);
    //货物上超限报警
    private static final PlcOffset CARGO_UP_ALARM = new PlcOffset(18, 10);
    //货物左超宽报警
    private static final PlcOffset CARGO_LEFT_ALARM = new PlcOffset(18, 11);
    //货物右超宽报警
    private static final PlcOffset CARGO_RIGHT_ALARM = new PlcOffset(18, 12);
    //货物前超限报警
    private static final PlcOffset CARGO_BEFORE_ALARM = new PlcOffset(18, 13);
    //货物后超限报警
    private static final PlcOffset CARGO_AFTER_ALARM = new PlcOffset(18, 14);
    //托盘无货物报警
    private static final PlcOffset PALLET_EMPTY_ALARM = new PlcOffset(18, 15);
    //高度1检测
    private static final PlcOffset HEIGHT_1 = new PlcOffset(19, 0);
    //高度2检测
    private static final PlcOffset HEIGHT_2 = new PlcOffset(19, 1);
    //高度3检测
    private static final PlcOffset HEIGHT_3 = new PlcOffset(19, 2);
    //外形检测可读
    private static final PlcOffset SHAPE_CAN_READ = new PlcOffset(19, 4);
    //叉车信号
    private static final PlcOffset FORK_CAR_SIGNAL = new PlcOffset(19, 5);
    //称重可读
    private static final PlcOffset WEIGHT_CAR_SIGNAL = new PlcOffset(19, 5);

    /*************************************************称重**********************************************************/

    private static final PlcOffset WEIGHT_DATA = new PlcOffset(58, null);

    private static final PlcOffset LIMIT_WEIGHT_DATA = new PlcOffset(59, null);

    public ShapeConveyorOper(PlcDeviceType plcDeviceType, int startNo, String deviceName, int deviceNo, Coord coord,
                             Location location, Coord lockLocation, long deviceId, Integer gridzMax,
                             Integer gridzRelative, int plcId) {
        super(plcDeviceType, startNo, deviceName, deviceNo, coord, location, lockLocation, deviceId, gridzMax,
                gridzRelative, plcId);
    }

    public Integer getDeviceType() {
        return readPlc(DEVICE_TYPE);
    }

    public Integer getMovement() {
        return readPlc(MOVEMENT);
    }

    public Integer getConveyorAutoSpeed() {
        return readPlc(CONVEYOR_AUTO_SPEED);
    }

    public Integer getConveyorAutoAcc() {
        return readPlc(CONVEYOR_AUTO_ACC);
    }

    public Integer getConveyorAutoDec() {
        return readPlc(CONVEYOR_AUTO_DEC);
    }

    public Integer getConveyorManualSpeed() {
        return readPlc(CONVEYOR_MANUAL_SPEED);
    }

    public Integer getConveyorManualAcc() {
        return readPlc(CONVEYOR_MANUAL_ACC);
    }

    public Integer getConveyorManualDec() {
        return readPlc(CONVEYOR_MANUAL_DEC);
    }

    public Integer getTaskAction() {
        return readPlc(TASK_ACTION);
    }

    public boolean isClearError() {
        return readPlc(CLEAR_ERROR) == 1;
    }

    public boolean isStopState() {
        return readPlc(STOP_STATE) == 1;
    }

    public boolean isControllerMode() {
        return readPlc(AUTO_MODE) == 1;
    }

    public boolean isManualForward() {
        return readPlc(MANUAL_FORWARD) == 1;
    }

    public boolean isManualReverse() {
        return readPlc(MANUAL_REVERSE) == 1;
    }

    public boolean isShapeCanRead() {
        return readPlc(SHAPE_CAN_READ) == 1;
    }

    public boolean isForkCarSignal() {
        return readPlc(FORK_CAR_SIGNAL) == 1;
    }

    /**
     * wcs可以下发任务
     *
     * @return
     */
    public boolean isControl() {
        return readPlc(IS_CONTROL) == 1;
    }

    public boolean isRunning() {
        return readPlc(RUNNING) == 1;
    }

    public boolean isErrorState() {
        return readPlc(ERROR_STATE) == 1;
    }

    /**
     * 入库光电
     *
     * @return
     */
    public boolean isInCargoSignalValid() {
        return readPlc(IN_CARGO_SIGNAL) == 1;
    }

    /**
     * 出库光电
     *
     * @return
     */
    public boolean isOutCargoSignalValid() {
        return readPlc(OUT_CARGO_SIGNAL) == 1;
    }

    public boolean isCargoUpAlarm() {
        return readPlc(CARGO_UP_ALARM) == 1;
    }

    public boolean isCargoLeftAlarm() {
        return readPlc(CARGO_LEFT_ALARM) == 1;
    }

    public boolean isCargoRightAlarm() {
        return readPlc(CARGO_RIGHT_ALARM) == 1;
    }

    public boolean isCargoBeforeAlarm() {
        return readPlc(CARGO_BEFORE_ALARM) == 1;
    }

    public boolean isCargoAfterAlarm() {
        return readPlc(CARGO_AFTER_ALARM) == 1;
    }

    public boolean isPalletEmptyAlarm() {
        return readPlc(PALLET_EMPTY_ALARM) == 1;
    }

    public boolean isHeight1() {
        return readPlc(HEIGHT_1) == 1;
    }

    public boolean isHeight2() {
        return readPlc(HEIGHT_2) == 1;
    }

    public boolean isHeight3() {
        return readPlc(HEIGHT_3) == 1;
    }

    public Integer getErrorInfo() {
        return readPlc(ERROR_INFO);
    }

    public Integer getCheckNo() {
        return readPlc(CHECK_NO);
    }

    public Integer getCurrentDeviceType() {
        return readPlc(CURRENT_DEVICE_TYPE);
    }

    public Integer getTaskNo() {
        return readPlc(TASK_NO);
    }

    public Integer getTaskStartLocation() {
        return readPlc(TASK_START_LOCATION);
    }

    public Integer getTaskEndLocation() {
        return readPlc(TASK_END_LOCATION);
    }

    public void sendConveryTask(int taskNo, int startDeviceNo, int endDeviceNo) throws IOException {
        String des = getDeviceStatus();
        while (getTaskNo() != 0) {
            setDeviceStatus("任务号不为0，不能下发任务：" + taskNo);
            WcsFuncUtil.delay(1000);
        }
        setDeviceStatus(des);

        LoggerUtil.info("下发输送任务，设备：" + getDeviceName() + ", 任务编号：" + taskNo + ", 起点设备：" + startDeviceNo
                + ", 终点设备:" + endDeviceNo, this.getClass());
        int[] data = new int[3];
        data[0] = taskNo;
        data[1] = startDeviceNo;
        data[2] = endDeviceNo;
        oper("发送行驶任务", this.getClass(), SEND_TASK, 10, data);
    }

    @PlcAnnotation("清除任务编号")
    public void clearTaskNo() throws IOException {
        LoggerUtil.info("清除任务编号，设备：" + getDeviceName(), this.getClass());
        oper("清除任务编号", this.getClass(), CLEAR_TASK, 10, 1);
    }

    /**
     * 检测入库信号
     *
     * @return
     */
    public boolean getIfAskInCargo() {
        return readPlc(ASK_IN_CARGO) == 1;
    }

    /**
     * 是否有货
     *
     * @return
     */
    public boolean hasCargo() {
        return isInCargoSignalValid() || isOutCargoSignalValid();
    }

    @PlcAnnotation(value = "WCS控制输送线工位运行")
    public void converyRun(SingleConveryRunType runType, Direction direction, ConveryTaskType out) throws IOException {
        oper(getDeviceName() + runType + direction, this.getClass(),
                TASK_ACTION.getAddressOffset(), 5,
                runType.getValue() * 100 + direction.getDirectionNum() * 10 + out.getValue());
    }

    /**
     * WCS清除故障
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "WCS清除故障")
    public void clearError() throws IOException {
        oper(getDeviceName() + "清除故障", this.getClass(),
                CLEAR_ERROR.getAddressOffset(), 5,
                1 << CLEAR_ERROR.getBitOffset());
    }

    /**
     * 开启外形检测
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "WCS卡其外形检测")
    public void openShape() throws IOException {
        oper(getDeviceName() + "开启外形检测", this.getClass(),
                OPEN_SHAPE.getAddressOffset(), 5,
                1 << OPEN_SHAPE.getBitOffset());
    }

    /**
     * 关闭外形检测
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "WCS关闭外形检测")
    public void closeShape() throws IOException {
        oper(getDeviceName() + "关闭外形检测", this.getClass(),
                CLOSE_SHAPE.getAddressOffset(), 5,
                1 << CLOSE_SHAPE.getBitOffset());
    }

    //扫码和外形检测同时开关

    /**
     * 开启扫码
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "开启扫码")
    public void openScan() throws IOException {
        getPlc().getModbusUtil().oper(getDeviceName() + "开启扫码", this.getClass(),
                10000 + OPEN_SHAPE.getAddressOffset(), 5, 1 << OPEN_SHAPE.getBitOffset());
    }

    /**
     * 关闭扫码
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "关闭扫码")
    public void closeScan() throws IOException {
        getPlc().getModbusUtil().oper(getDeviceName() + "关闭扫码", this.getClass(),
                10000 + CLOSE_SHAPE.getAddressOffset(), 5, 1 << CLOSE_SHAPE.getBitOffset());
    }

    public int getHeightType() {
        if (isHeight3()) {
            return 3;
        } else if (isHeight2()) {
            return 2;
        } else if (isHeight1()) {
            return 1;
        }

        return 0;
    }

    /**
     * 设置扫码故障
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "设置扫码故障")
    public void setScanError() throws IOException {
        oper(getDeviceName() + "设置扫码故障", this.getClass(),
                SCAN_ERROR.getAddressOffset(), 5,
                1 << SCAN_ERROR.getBitOffset());
    }

    /**
     * 清除扫码故障
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "清除扫码故障")
    public void clearScanError() throws IOException {
        oper(getDeviceName() + "清除扫码故障", this.getClass(),
                CLEAR_SCAN_ERROR.getAddressOffset(), 5,
                1 << CLEAR_SCAN_ERROR.getBitOffset());
    }

    /**
     * 设置无任务故障
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "设置无任务故障")
    public void setNoTaskError() throws IOException {
        oper(getDeviceName() + "设置无任务故障", this.getClass(),
                NO_TASK_ERROR.getAddressOffset(), 5,
                1 << NO_TASK_ERROR.getBitOffset());
    }

    /**
     * 清除无任务故障
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "清除无任务故障")
    public void clearNoTaskError() throws IOException {
        oper(getDeviceName() + "清除无任务故障", this.getClass(),
                CLEAR_NO_TASK_ERROR.getAddressOffset(), 5,
                1 << CLEAR_NO_TASK_ERROR.getBitOffset());
    }

    /**
     * 设置称重故障
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "设置称重故障")
    public void setWeightError() throws IOException {
        oper(getDeviceName() + "设置称重故障", this.getClass(),
                WEIGHT_ERROR.getAddressOffset(), 5,
                1 << WEIGHT_ERROR.getBitOffset());
    }

    /**
     * 清除称重故障
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "清除称重故障")
    public void clearWeightError() throws IOException {
        oper(getDeviceName() + "清除称重故障", this.getClass(),
                CLEAR_WEIGHT_ERROR.getAddressOffset(), 5,
                1 << CLEAR_WEIGHT_ERROR.getBitOffset());
    }

    /**
     * 清除入库信号
     *
     * @throws IOException
     */
    @PlcAnnotation(value = "WCS清除入库信号")
    public void clearAskInCargo() throws IOException {
        oper(getDeviceName() + "清除入库信号", this.getClass(),
                CLEAR_ASK_IN_CARGO.getAddressOffset(), 5,
                1 << CLEAR_ASK_IN_CARGO.getBitOffset());
    }

    /**
     * 设置设备类型
     */
    @PlcAnnotation(value = "设置设备类型")
    public void setDeviceType() throws IOException {
        oper("设置设备类型", this.getClass(), DEVICE_TYPE.getAddressOffset(), 5, 3);
    }


    /**
     * 设置运行速度
     *
     * @param speed
     */
    @PlcAnnotation(value = "WCS设置运行速度")
    public void setConveyorAutoSpeed(Integer speed) throws IOException {
        oper(getDeviceName() + "设置运行速度", this.getClass(),
                CONVEYOR_AUTO_SPEED.getAddressOffset(), 5, speed);
    }

    /**
     * 设置加速度
     *
     * @param speed
     */
    @PlcAnnotation(value = "WCS设置加速度")
    public void setConveyorAutoAcc(Integer speed) throws IOException {
        oper(getDeviceName() + "设置加速度", this.getClass(),
                CONVEYOR_AUTO_ACC.getAddressOffset(), 5, speed);
    }

    /**
     * 设置减速度
     *
     * @param speed
     */
    @PlcAnnotation(value = "WCS设置减速度")
    public void setConveyorAutoDec(Integer speed) throws IOException {
        oper(getDeviceName() + "设置减速度", this.getClass(),
                CONVEYOR_AUTO_DEC.getAddressOffset(), 5, speed);
    }

    /**
     * 设置手动速度
     *
     * @param speed
     */
    @PlcAnnotation(value = "WCS设置手动速度")
    public void setConveyorManualSpeed(Integer speed) throws IOException {
        oper(getDeviceName() + "设置手动速度", this.getClass(),
                CONVEYOR_MANUAL_SPEED.getAddressOffset(), 5, speed);
    }

    /**
     * 设置手动加速度
     *
     * @param speed
     */
    @PlcAnnotation(value = "WCS设置手动加速度")
    public void setConveyorManualAcc(Integer speed) throws IOException {
        oper(getDeviceName() + "设置手动加速度", this.getClass(),
                CONVEYOR_MANUAL_ACC.getAddressOffset(), 5, speed);
    }

    /**
     * 设置手动减速度
     *
     * @param speed
     */
    @PlcAnnotation(value = "WCS设置手动减速度")
    public void setConveyorManualDec(Integer speed) throws IOException {
        oper(getDeviceName() + "设置手动减速度", this.getClass(),
                CONVEYOR_MANUAL_DEC.getAddressOffset(), 5, speed);
    }

    public void clearTask() throws IOException {
        converyRun(SingleConveryRunType.CONVERY, Direction.SINGLE_CONVERY_STOP, ConveryTaskType.STOP);
    }

    @Override
    public String toString() {
        return "deviceName=" + getDeviceName() +
                "inCargoSignal=" + isInCargoSignalValid() +
                "outCargoSignal=" + isOutCargoSignalValid() +
                "isControl=" + isControl() +
                "askInCargo=" + getIfAskInCargo()
                ;
    }

//    public int getTaskNo() {
//        return getTask();
//    }

//    public void setTaskNo(int taskNo) {
//        setTask(taskNo);
//    }

    /**
     * 设置延迟时间
     *
     * @param speed
     */
    @PlcAnnotation(value = "WCS设置延迟时间")
    public void setDelayTime(Integer speed) throws IOException {
        oper(getDeviceName() + "设置延迟时间", this.getClass(),
                CONVERY_STOP_DELAY_TIME.getAddressOffset(), 5, speed * 100);
    }

    public int getResult() {
        if (isCargoUpAlarm() || isCargoBeforeAlarm() || isCargoAfterAlarm() || isCargoLeftAlarm()
                || isCargoRightAlarm()) {
            return 2;
        }

        return 1;
    }

    public boolean isScanCanRead() {
        return readPlc(SCAN_CAN_READ) == 1;
    }

    /**
     * 获取扫码结果
     * 起始字节为扫码值有效值，后面15个字节用于码值解析
     * 码值解析按byte读取，0x2为起始标志，0x3为结束标志，中间为实际码值
     *
     * @return 条码值
     */
    public String getCodeData() {
        StringBuilder sb = new StringBuilder();
        int code_value;
        boolean allow_analyze_code = false;
//        PlcOffset plcOffset = new PlcOffset(getStartNo(), null);
        for (int i = 1; i <= maxBarCode; i++) {
            for (int j = 0; j < 2; j++) {
                code_value = getBarCodeByBit(i, ((j % 2) == 0) ? highBit : lowBit);
//                plcOffset.setAddressOffset(getStartNo() + i);
//                code_value = readPlc(plcOffset);

                if (!allow_analyze_code) {
                    if (code_value == 02) {
                        allow_analyze_code = true;
                    }
                } else {
                    if (code_value == 3) {
                        break;
                    }
                    sb.append((char) code_value);
                }
            }
        }
        return sb.toString();
    }

    public boolean isCanWeightRead() {
        return readPlc(WEIGHT_CAR_SIGNAL) == 1;
    }

    //f:0.1kg
    public float getWeightData() {
        return ((float) readPlc(WEIGHT_DATA)) / 10;
    }

    //f:0.1kg
    public float getLimitWeightData() {
        return ((float) readPlc(LIMIT_WEIGHT_DATA)) / 10;
    }
}
